// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/child/service_worker/service_worker_dispatcher.h"

#include <stddef.h>
#include <utility>

#include "base/lazy_instance.h"
#include "base/memory/ptr_util.h"
#include "base/single_thread_task_runner.h"
#include "base/stl_util.h"
#include "base/threading/thread_local.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/trace_event/trace_event.h"
#include "content/child/service_worker/service_worker_handle_reference.h"
#include "content/child/service_worker/service_worker_provider_context.h"
#include "content/child/service_worker/service_worker_registration_handle_reference.h"
#include "content/child/service_worker/web_service_worker_impl.h"
#include "content/child/service_worker/web_service_worker_registration_impl.h"
#include "content/child/thread_safe_sender.h"
#include "content/child/webmessageportchannel_impl.h"
#include "content/common/service_worker/service_worker_messages.h"
#include "content/common/service_worker/service_worker_types.h"
#include "content/public/common/content_constants.h"
#include "third_party/WebKit/public/platform/WebString.h"
#include "third_party/WebKit/public/platform/modules/serviceworker/WebNavigationPreloadState.h"
#include "third_party/WebKit/public/platform/modules/serviceworker/WebServiceWorkerProviderClient.h"
#include "url/url_constants.h"

using base::ThreadLocalPointer;
using blink::WebServiceWorkerError;
using blink::WebServiceWorkerProvider;

namespace content {

namespace {

    base::LazyInstance<ThreadLocalPointer<void>>::Leaky g_dispatcher_tls = LAZY_INSTANCE_INITIALIZER;

    void* const kHasBeenDeleted = reinterpret_cast<void*>(0x1);

    int CurrentWorkerId()
    {
        return WorkerThread::GetCurrentId();
    }

} // namespace

ServiceWorkerDispatcher::ServiceWorkerDispatcher(
    ThreadSafeSender* thread_safe_sender,
    base::SingleThreadTaskRunner* main_thread_task_runner)
    : thread_safe_sender_(thread_safe_sender)
    , main_thread_task_runner_(main_thread_task_runner)
{
    g_dispatcher_tls.Pointer()->Set(static_cast<void*>(this));
}

ServiceWorkerDispatcher::~ServiceWorkerDispatcher()
{
    g_dispatcher_tls.Pointer()->Set(kHasBeenDeleted);
}

void ServiceWorkerDispatcher::OnMessageReceived(const IPC::Message& msg)
{
    bool handled = true;

    // When you add a new message handler, you should consider adding a similar
    // handler in ServiceWorkerMessageFilter to release references passed from
    // the browser process in case we fail to post task to the thread.
    IPC_BEGIN_MESSAGE_MAP(ServiceWorkerDispatcher, msg)
    IPC_MESSAGE_HANDLER(ServiceWorkerMsg_AssociateRegistration,
        OnAssociateRegistration)
    IPC_MESSAGE_HANDLER(ServiceWorkerMsg_DisassociateRegistration,
        OnDisassociateRegistration)
    IPC_MESSAGE_HANDLER(ServiceWorkerMsg_ServiceWorkerRegistered, OnRegistered)
    IPC_MESSAGE_HANDLER(ServiceWorkerMsg_ServiceWorkerUpdated, OnUpdated)
    IPC_MESSAGE_HANDLER(ServiceWorkerMsg_ServiceWorkerUnregistered,
        OnUnregistered)
    IPC_MESSAGE_HANDLER(ServiceWorkerMsg_DidGetRegistration,
        OnDidGetRegistration)
    IPC_MESSAGE_HANDLER(ServiceWorkerMsg_DidGetRegistrations,
        OnDidGetRegistrations)
    IPC_MESSAGE_HANDLER(ServiceWorkerMsg_DidGetRegistrationForReady,
        OnDidGetRegistrationForReady)
    IPC_MESSAGE_HANDLER(ServiceWorkerMsg_DidEnableNavigationPreload,
        OnDidEnableNavigationPreload)
    IPC_MESSAGE_HANDLER(ServiceWorkerMsg_DidGetNavigationPreloadState,
        OnDidGetNavigationPreloadState)
    IPC_MESSAGE_HANDLER(ServiceWorkerMsg_DidSetNavigationPreloadHeader,
        OnDidSetNavigationPreloadHeader)
    IPC_MESSAGE_HANDLER(ServiceWorkerMsg_ServiceWorkerRegistrationError,
        OnRegistrationError)
    IPC_MESSAGE_HANDLER(ServiceWorkerMsg_ServiceWorkerUpdateError,
        OnUpdateError)
    IPC_MESSAGE_HANDLER(ServiceWorkerMsg_ServiceWorkerUnregistrationError,
        OnUnregistrationError)
    IPC_MESSAGE_HANDLER(ServiceWorkerMsg_ServiceWorkerGetRegistrationError,
        OnGetRegistrationError)
    IPC_MESSAGE_HANDLER(ServiceWorkerMsg_ServiceWorkerGetRegistrationsError,
        OnGetRegistrationsError)
    IPC_MESSAGE_HANDLER(ServiceWorkerMsg_EnableNavigationPreloadError,
        OnEnableNavigationPreloadError)
    IPC_MESSAGE_HANDLER(ServiceWorkerMsg_GetNavigationPreloadStateError,
        OnGetNavigationPreloadStateError)
    IPC_MESSAGE_HANDLER(ServiceWorkerMsg_SetNavigationPreloadHeaderError,
        OnSetNavigationPreloadHeaderError)
    IPC_MESSAGE_HANDLER(ServiceWorkerMsg_ServiceWorkerStateChanged,
        OnServiceWorkerStateChanged)
    IPC_MESSAGE_HANDLER(ServiceWorkerMsg_SetVersionAttributes,
        OnSetVersionAttributes)
    IPC_MESSAGE_HANDLER(ServiceWorkerMsg_UpdateFound,
        OnUpdateFound)
    IPC_MESSAGE_HANDLER(ServiceWorkerMsg_SetControllerServiceWorker,
        OnSetControllerServiceWorker)
    IPC_MESSAGE_HANDLER(ServiceWorkerMsg_MessageToDocument,
        OnPostMessage)
    IPC_MESSAGE_UNHANDLED(handled = false)
    IPC_END_MESSAGE_MAP()
    DCHECK(handled) << "Unhandled message:" << msg.type();
}

void ServiceWorkerDispatcher::RegisterServiceWorker(
    int provider_id,
    const GURL& pattern,
    const GURL& script_url,
    std::unique_ptr<WebServiceWorkerRegistrationCallbacks> callbacks)
{
    DCHECK(callbacks);

    if (pattern.possibly_invalid_spec().size() > url::kMaxURLChars || script_url.possibly_invalid_spec().size() > url::kMaxURLChars) {
        std::string error_message(kServiceWorkerRegisterErrorPrefix);
        error_message += "The provided scriptURL or scope is too long.";
        callbacks->onError(
            WebServiceWorkerError(WebServiceWorkerError::ErrorTypeSecurity,
                blink::WebString::fromASCII(error_message)));
        return;
    }

    int request_id = pending_registration_callbacks_.Add(std::move(callbacks));
    TRACE_EVENT_ASYNC_BEGIN2("ServiceWorker",
        "ServiceWorkerDispatcher::RegisterServiceWorker",
        request_id,
        "Scope", pattern.spec(),
        "Script URL", script_url.spec());
    thread_safe_sender_->Send(new ServiceWorkerHostMsg_RegisterServiceWorker(
        CurrentWorkerId(), request_id, provider_id, pattern, script_url));
}

void ServiceWorkerDispatcher::UpdateServiceWorker(
    int provider_id,
    int64_t registration_id,
    std::unique_ptr<WebServiceWorkerUpdateCallbacks> callbacks)
{
    DCHECK(callbacks);
    int request_id = pending_update_callbacks_.Add(std::move(callbacks));
    thread_safe_sender_->Send(new ServiceWorkerHostMsg_UpdateServiceWorker(
        CurrentWorkerId(), request_id, provider_id, registration_id));
}

void ServiceWorkerDispatcher::UnregisterServiceWorker(
    int provider_id,
    int64_t registration_id,
    std::unique_ptr<WebServiceWorkerUnregistrationCallbacks> callbacks)
{
    DCHECK(callbacks);
    int request_id = pending_unregistration_callbacks_.Add(std::move(callbacks));
    TRACE_EVENT_ASYNC_BEGIN1("ServiceWorker",
        "ServiceWorkerDispatcher::UnregisterServiceWorker",
        request_id, "Registration ID", registration_id);
    thread_safe_sender_->Send(new ServiceWorkerHostMsg_UnregisterServiceWorker(
        CurrentWorkerId(), request_id, provider_id, registration_id));
}

void ServiceWorkerDispatcher::GetRegistration(
    int provider_id,
    const GURL& document_url,
    std::unique_ptr<WebServiceWorkerGetRegistrationCallbacks> callbacks)
{
    DCHECK(callbacks);

    if (document_url.possibly_invalid_spec().size() > url::kMaxURLChars) {
        std::string error_message(kServiceWorkerGetRegistrationErrorPrefix);
        error_message += "The provided documentURL is too long.";
        callbacks->onError(
            WebServiceWorkerError(WebServiceWorkerError::ErrorTypeSecurity,
                blink::WebString::fromASCII(error_message)));
        return;
    }

    int request_id = pending_get_registration_callbacks_.Add(std::move(callbacks));
    TRACE_EVENT_ASYNC_BEGIN1("ServiceWorker",
        "ServiceWorkerDispatcher::GetRegistration",
        request_id,
        "Document URL", document_url.spec());
    thread_safe_sender_->Send(new ServiceWorkerHostMsg_GetRegistration(
        CurrentWorkerId(), request_id, provider_id, document_url));
}

void ServiceWorkerDispatcher::GetRegistrations(
    int provider_id,
    std::unique_ptr<WebServiceWorkerGetRegistrationsCallbacks> callbacks)
{
    DCHECK(callbacks);

    int request_id = pending_get_registrations_callbacks_.Add(std::move(callbacks));

    TRACE_EVENT_ASYNC_BEGIN0("ServiceWorker",
        "ServiceWorkerDispatcher::GetRegistrations",
        request_id);
    thread_safe_sender_->Send(new ServiceWorkerHostMsg_GetRegistrations(
        CurrentWorkerId(), request_id, provider_id));
}

void ServiceWorkerDispatcher::GetRegistrationForReady(
    int provider_id,
    std::unique_ptr<WebServiceWorkerGetRegistrationForReadyCallbacks>
        callbacks)
{
    int request_id = get_for_ready_callbacks_.Add(std::move(callbacks));
    TRACE_EVENT_ASYNC_BEGIN0("ServiceWorker",
        "ServiceWorkerDispatcher::GetRegistrationForReady",
        request_id);
    thread_safe_sender_->Send(new ServiceWorkerHostMsg_GetRegistrationForReady(
        CurrentWorkerId(), request_id, provider_id));
}

void ServiceWorkerDispatcher::EnableNavigationPreload(
    int provider_id,
    int64_t registration_id,
    bool enable,
    std::unique_ptr<WebEnableNavigationPreloadCallbacks> callbacks)
{
    DCHECK(callbacks);
    int request_id = enable_navigation_preload_callbacks_.Add(std::move(callbacks));
    thread_safe_sender_->Send(new ServiceWorkerHostMsg_EnableNavigationPreload(
        CurrentWorkerId(), request_id, provider_id, registration_id, enable));
}

void ServiceWorkerDispatcher::GetNavigationPreloadState(
    int provider_id,
    int64_t registration_id,
    std::unique_ptr<WebGetNavigationPreloadStateCallbacks> callbacks)
{
    DCHECK(callbacks);
    int request_id = get_navigation_preload_state_callbacks_.Add(std::move(callbacks));
    thread_safe_sender_->Send(new ServiceWorkerHostMsg_GetNavigationPreloadState(
        CurrentWorkerId(), request_id, provider_id, registration_id));
}

void ServiceWorkerDispatcher::SetNavigationPreloadHeader(
    int provider_id,
    int64_t registration_id,
    const std::string& value,
    std::unique_ptr<WebSetNavigationPreloadHeaderCallbacks> callbacks)
{
    DCHECK(callbacks);
    int request_id = set_navigation_preload_header_callbacks_.Add(std::move(callbacks));
    thread_safe_sender_->Send(new ServiceWorkerHostMsg_SetNavigationPreloadHeader(
        CurrentWorkerId(), request_id, provider_id, registration_id, value));
}

void ServiceWorkerDispatcher::AddProviderContext(
    ServiceWorkerProviderContext* provider_context)
{
    DCHECK(provider_context);
    int provider_id = provider_context->provider_id();
    DCHECK(!base::ContainsKey(provider_contexts_, provider_id));
    provider_contexts_[provider_id] = provider_context;
}

void ServiceWorkerDispatcher::RemoveProviderContext(
    ServiceWorkerProviderContext* provider_context)
{
    DCHECK(provider_context);
    DCHECK(
        base::ContainsKey(provider_contexts_, provider_context->provider_id()));
    provider_contexts_.erase(provider_context->provider_id());
}

void ServiceWorkerDispatcher::AddProviderClient(
    int provider_id,
    blink::WebServiceWorkerProviderClient* client)
{
    DCHECK(client);
    DCHECK(!base::ContainsKey(provider_clients_, provider_id));
    provider_clients_[provider_id] = client;
}

void ServiceWorkerDispatcher::RemoveProviderClient(int provider_id)
{
    // This could be possibly called multiple times to ensure termination.
    if (base::ContainsKey(provider_clients_, provider_id))
        provider_clients_.erase(provider_id);
}

ServiceWorkerDispatcher*
ServiceWorkerDispatcher::GetOrCreateThreadSpecificInstance(
    ThreadSafeSender* thread_safe_sender,
    base::SingleThreadTaskRunner* main_thread_task_runner)
{
    if (g_dispatcher_tls.Pointer()->Get() == kHasBeenDeleted) {
        NOTREACHED() << "Re-instantiating TLS ServiceWorkerDispatcher.";
        g_dispatcher_tls.Pointer()->Set(NULL);
    }
    if (g_dispatcher_tls.Pointer()->Get())
        return static_cast<ServiceWorkerDispatcher*>(
            g_dispatcher_tls.Pointer()->Get());

    ServiceWorkerDispatcher* dispatcher = new ServiceWorkerDispatcher(thread_safe_sender, main_thread_task_runner);
    if (WorkerThread::GetCurrentId())
        WorkerThread::AddObserver(dispatcher);
    return dispatcher;
}

ServiceWorkerDispatcher* ServiceWorkerDispatcher::GetThreadSpecificInstance()
{
    if (g_dispatcher_tls.Pointer()->Get() == kHasBeenDeleted)
        return NULL;
    return static_cast<ServiceWorkerDispatcher*>(
        g_dispatcher_tls.Pointer()->Get());
}

void ServiceWorkerDispatcher::WillStopCurrentWorkerThread()
{
    delete this;
}

scoped_refptr<WebServiceWorkerImpl>
ServiceWorkerDispatcher::GetOrCreateServiceWorker(
    std::unique_ptr<ServiceWorkerHandleReference> handle_ref)
{
    if (!handle_ref)
        return nullptr;

    WorkerObjectMap::iterator found = service_workers_.find(handle_ref->handle_id());
    if (found != service_workers_.end())
        return found->second;

    // WebServiceWorkerImpl constructor calls AddServiceWorker.
    return new WebServiceWorkerImpl(std::move(handle_ref),
        thread_safe_sender_.get());
}

scoped_refptr<WebServiceWorkerRegistrationImpl>
ServiceWorkerDispatcher::GetOrCreateRegistration(
    const ServiceWorkerRegistrationObjectInfo& info,
    const ServiceWorkerVersionAttributes& attrs)
{
    RegistrationObjectMap::iterator found = registrations_.find(info.handle_id);
    if (found != registrations_.end())
        return found->second;

    // WebServiceWorkerRegistrationImpl constructor calls
    // AddServiceWorkerRegistration.
    scoped_refptr<WebServiceWorkerRegistrationImpl> registration(
        new WebServiceWorkerRegistrationImpl(
            ServiceWorkerRegistrationHandleReference::Create(
                info, thread_safe_sender_.get())));

    registration->SetInstalling(
        GetOrCreateServiceWorker(ServiceWorkerHandleReference::Create(
            attrs.installing, thread_safe_sender_.get())));
    registration->SetWaiting(
        GetOrCreateServiceWorker(ServiceWorkerHandleReference::Create(
            attrs.waiting, thread_safe_sender_.get())));
    registration->SetActive(
        GetOrCreateServiceWorker(ServiceWorkerHandleReference::Create(
            attrs.active, thread_safe_sender_.get())));
    return registration;
}

scoped_refptr<WebServiceWorkerRegistrationImpl>
ServiceWorkerDispatcher::GetOrAdoptRegistration(
    const ServiceWorkerRegistrationObjectInfo& info,
    const ServiceWorkerVersionAttributes& attrs)
{
    std::unique_ptr<ServiceWorkerRegistrationHandleReference> registration_ref = Adopt(info);
    std::unique_ptr<ServiceWorkerHandleReference> installing_ref = Adopt(attrs.installing);
    std::unique_ptr<ServiceWorkerHandleReference> waiting_ref = Adopt(attrs.waiting);
    std::unique_ptr<ServiceWorkerHandleReference> active_ref = Adopt(attrs.active);

    RegistrationObjectMap::iterator found = registrations_.find(info.handle_id);
    if (found != registrations_.end())
        return found->second;

    // WebServiceWorkerRegistrationImpl constructor calls
    // AddServiceWorkerRegistration.
    scoped_refptr<WebServiceWorkerRegistrationImpl> registration(
        new WebServiceWorkerRegistrationImpl(std::move(registration_ref)));
    registration->SetInstalling(
        GetOrCreateServiceWorker(std::move(installing_ref)));
    registration->SetWaiting(GetOrCreateServiceWorker(std::move(waiting_ref)));
    registration->SetActive(GetOrCreateServiceWorker(std::move(active_ref)));
    return registration;
}

void ServiceWorkerDispatcher::OnAssociateRegistration(
    int thread_id,
    int provider_id,
    const ServiceWorkerRegistrationObjectInfo& info,
    const ServiceWorkerVersionAttributes& attrs)
{
    // Adopt the references sent from the browser process and pass them to the
    // provider context if it exists.
    std::unique_ptr<ServiceWorkerRegistrationHandleReference> registration = Adopt(info);
    std::unique_ptr<ServiceWorkerHandleReference> installing = Adopt(attrs.installing);
    std::unique_ptr<ServiceWorkerHandleReference> waiting = Adopt(attrs.waiting);
    std::unique_ptr<ServiceWorkerHandleReference> active = Adopt(attrs.active);
    ProviderContextMap::iterator context = provider_contexts_.find(provider_id);
    if (context != provider_contexts_.end()) {
        context->second->OnAssociateRegistration(
            std::move(registration), std::move(installing), std::move(waiting),
            std::move(active));
    }
}

void ServiceWorkerDispatcher::OnDisassociateRegistration(
    int thread_id,
    int provider_id)
{
    ProviderContextMap::iterator provider = provider_contexts_.find(provider_id);
    if (provider == provider_contexts_.end())
        return;
    provider->second->OnDisassociateRegistration();
}

void ServiceWorkerDispatcher::OnRegistered(
    int thread_id,
    int request_id,
    const ServiceWorkerRegistrationObjectInfo& info,
    const ServiceWorkerVersionAttributes& attrs)
{
    TRACE_EVENT_ASYNC_STEP_INTO0("ServiceWorker",
        "ServiceWorkerDispatcher::RegisterServiceWorker",
        request_id,
        "OnRegistered");
    TRACE_EVENT_ASYNC_END0("ServiceWorker",
        "ServiceWorkerDispatcher::RegisterServiceWorker",
        request_id);
    WebServiceWorkerRegistrationCallbacks* callbacks = pending_registration_callbacks_.Lookup(request_id);
    DCHECK(callbacks);
    if (!callbacks)
        return;

    callbacks->onSuccess(WebServiceWorkerRegistrationImpl::CreateHandle(
        GetOrAdoptRegistration(info, attrs)));
    pending_registration_callbacks_.Remove(request_id);
}

void ServiceWorkerDispatcher::OnUpdated(int thread_id, int request_id)
{
    TRACE_EVENT_ASYNC_STEP_INTO0("ServiceWorker",
        "ServiceWorkerDispatcher::UpdateServiceWorker",
        request_id, "OnUpdated");
    TRACE_EVENT_ASYNC_END0("ServiceWorker",
        "ServiceWorkerDispatcher::UpdateServiceWorker",
        request_id);
    WebServiceWorkerUpdateCallbacks* callbacks = pending_update_callbacks_.Lookup(request_id);
    DCHECK(callbacks);
    if (!callbacks)
        return;

    callbacks->onSuccess();
    pending_update_callbacks_.Remove(request_id);
}

void ServiceWorkerDispatcher::OnUnregistered(int thread_id,
    int request_id,
    bool is_success)
{
    TRACE_EVENT_ASYNC_STEP_INTO0(
        "ServiceWorker",
        "ServiceWorkerDispatcher::UnregisterServiceWorker",
        request_id,
        "OnUnregistered");
    TRACE_EVENT_ASYNC_END0("ServiceWorker",
        "ServiceWorkerDispatcher::UnregisterServiceWorker",
        request_id);
    WebServiceWorkerUnregistrationCallbacks* callbacks = pending_unregistration_callbacks_.Lookup(request_id);
    DCHECK(callbacks);
    if (!callbacks)
        return;
    callbacks->onSuccess(is_success);
    pending_unregistration_callbacks_.Remove(request_id);
}

void ServiceWorkerDispatcher::OnDidGetRegistration(
    int thread_id,
    int request_id,
    const ServiceWorkerRegistrationObjectInfo& info,
    const ServiceWorkerVersionAttributes& attrs)
{
    TRACE_EVENT_ASYNC_STEP_INTO0(
        "ServiceWorker",
        "ServiceWorkerDispatcher::GetRegistration",
        request_id,
        "OnDidGetRegistration");
    TRACE_EVENT_ASYNC_END0("ServiceWorker",
        "ServiceWorkerDispatcher::GetRegistration",
        request_id);
    WebServiceWorkerGetRegistrationCallbacks* callbacks = pending_get_registration_callbacks_.Lookup(request_id);
    DCHECK(callbacks);
    if (!callbacks)
        return;

    scoped_refptr<WebServiceWorkerRegistrationImpl> registration;
    if (info.handle_id != kInvalidServiceWorkerRegistrationHandleId)
        registration = GetOrAdoptRegistration(info, attrs);

    callbacks->onSuccess(
        WebServiceWorkerRegistrationImpl::CreateHandle(registration));
    pending_get_registration_callbacks_.Remove(request_id);
}

void ServiceWorkerDispatcher::OnDidGetRegistrations(
    int thread_id,
    int request_id,
    const std::vector<ServiceWorkerRegistrationObjectInfo>& infos,
    const std::vector<ServiceWorkerVersionAttributes>& attrs)
{
    TRACE_EVENT_ASYNC_STEP_INTO0(
        "ServiceWorker",
        "ServiceWorkerDispatcher::GetRegistrations",
        request_id,
        "OnDidGetRegistrations");
    TRACE_EVENT_ASYNC_END0("ServiceWorker",
        "ServiceWorkerDispatcher::GetRegistrations",
        request_id);

    WebServiceWorkerGetRegistrationsCallbacks* callbacks = pending_get_registrations_callbacks_.Lookup(request_id);
    DCHECK(callbacks);
    if (!callbacks)
        return;

    using WebServiceWorkerRegistrationHandles = WebServiceWorkerProvider::WebServiceWorkerRegistrationHandles;
    std::unique_ptr<WebServiceWorkerRegistrationHandles> registrations = base::MakeUnique<WebServiceWorkerRegistrationHandles>(infos.size());
    for (size_t i = 0; i < infos.size(); ++i) {
        if (infos[i].handle_id == kInvalidServiceWorkerHandleId)
            continue;
        (*registrations)[i] = WebServiceWorkerRegistrationImpl::CreateHandle(
            GetOrAdoptRegistration(infos[i], attrs[i]));
    }

    callbacks->onSuccess(std::move(registrations));
    pending_get_registrations_callbacks_.Remove(request_id);
}

void ServiceWorkerDispatcher::OnDidGetRegistrationForReady(
    int thread_id,
    int request_id,
    const ServiceWorkerRegistrationObjectInfo& info,
    const ServiceWorkerVersionAttributes& attrs)
{
    TRACE_EVENT_ASYNC_STEP_INTO0(
        "ServiceWorker",
        "ServiceWorkerDispatcher::GetRegistrationForReady",
        request_id,
        "OnDidGetRegistrationForReady");
    TRACE_EVENT_ASYNC_END0("ServiceWorker",
        "ServiceWorkerDispatcher::GetRegistrationForReady",
        request_id);
    WebServiceWorkerGetRegistrationForReadyCallbacks* callbacks = get_for_ready_callbacks_.Lookup(request_id);
    DCHECK(callbacks);
    if (!callbacks)
        return;

    callbacks->onSuccess(WebServiceWorkerRegistrationImpl::CreateHandle(
        GetOrAdoptRegistration(info, attrs)));
    get_for_ready_callbacks_.Remove(request_id);
}

void ServiceWorkerDispatcher::OnDidEnableNavigationPreload(int thread_id,
    int request_id)
{
    WebEnableNavigationPreloadCallbacks* callbacks = enable_navigation_preload_callbacks_.Lookup(request_id);
    DCHECK(callbacks);
    if (!callbacks)
        return;
    callbacks->onSuccess();
    enable_navigation_preload_callbacks_.Remove(request_id);
}

void ServiceWorkerDispatcher::OnDidGetNavigationPreloadState(
    int thread_id,
    int request_id,
    const NavigationPreloadState& state)
{
    WebGetNavigationPreloadStateCallbacks* callbacks = get_navigation_preload_state_callbacks_.Lookup(request_id);
    DCHECK(callbacks);
    if (!callbacks)
        return;
    callbacks->onSuccess(blink::WebNavigationPreloadState(
        state.enabled, blink::WebString::fromUTF8(state.header)));
    get_navigation_preload_state_callbacks_.Remove(request_id);
}

void ServiceWorkerDispatcher::OnDidSetNavigationPreloadHeader(int thread_id,
    int request_id)
{
    WebSetNavigationPreloadHeaderCallbacks* callbacks = set_navigation_preload_header_callbacks_.Lookup(request_id);
    DCHECK(callbacks);
    if (!callbacks)
        return;
    callbacks->onSuccess();
    set_navigation_preload_header_callbacks_.Remove(request_id);
}

void ServiceWorkerDispatcher::OnRegistrationError(
    int thread_id,
    int request_id,
    WebServiceWorkerError::ErrorType error_type,
    const base::string16& message)
{
    TRACE_EVENT_ASYNC_STEP_INTO0("ServiceWorker",
        "ServiceWorkerDispatcher::RegisterServiceWorker",
        request_id,
        "OnRegistrationError");
    TRACE_EVENT_ASYNC_END0("ServiceWorker",
        "ServiceWorkerDispatcher::RegisterServiceWorker",
        request_id);
    WebServiceWorkerRegistrationCallbacks* callbacks = pending_registration_callbacks_.Lookup(request_id);
    DCHECK(callbacks);
    if (!callbacks)
        return;

    callbacks->onError(
        WebServiceWorkerError(error_type, blink::WebString::fromUTF16(message)));
    pending_registration_callbacks_.Remove(request_id);
}

void ServiceWorkerDispatcher::OnUpdateError(
    int thread_id,
    int request_id,
    WebServiceWorkerError::ErrorType error_type,
    const base::string16& message)
{
    TRACE_EVENT_ASYNC_STEP_INTO0("ServiceWorker",
        "ServiceWorkerDispatcher::UpdateServiceWorker",
        request_id, "OnUpdateError");
    TRACE_EVENT_ASYNC_END0("ServiceWorker",
        "ServiceWorkerDispatcher::UpdateServiceWorker",
        request_id);
    WebServiceWorkerUpdateCallbacks* callbacks = pending_update_callbacks_.Lookup(request_id);
    DCHECK(callbacks);
    if (!callbacks)
        return;

    callbacks->onError(
        WebServiceWorkerError(error_type, blink::WebString::fromUTF16(message)));
    pending_update_callbacks_.Remove(request_id);
}

void ServiceWorkerDispatcher::OnUnregistrationError(
    int thread_id,
    int request_id,
    WebServiceWorkerError::ErrorType error_type,
    const base::string16& message)
{
    TRACE_EVENT_ASYNC_STEP_INTO0(
        "ServiceWorker",
        "ServiceWorkerDispatcher::UnregisterServiceWorker",
        request_id,
        "OnUnregistrationError");
    TRACE_EVENT_ASYNC_END0("ServiceWorker",
        "ServiceWorkerDispatcher::UnregisterServiceWorker",
        request_id);
    WebServiceWorkerUnregistrationCallbacks* callbacks = pending_unregistration_callbacks_.Lookup(request_id);
    DCHECK(callbacks);
    if (!callbacks)
        return;

    callbacks->onError(
        WebServiceWorkerError(error_type, blink::WebString::fromUTF16(message)));
    pending_unregistration_callbacks_.Remove(request_id);
}

void ServiceWorkerDispatcher::OnGetRegistrationError(
    int thread_id,
    int request_id,
    WebServiceWorkerError::ErrorType error_type,
    const base::string16& message)
{
    TRACE_EVENT_ASYNC_STEP_INTO0(
        "ServiceWorker",
        "ServiceWorkerDispatcher::GetRegistration",
        request_id,
        "OnGetRegistrationError");
    TRACE_EVENT_ASYNC_END0("ServiceWorker",
        "ServiceWorkerDispatcher::GetRegistration",
        request_id);
    WebServiceWorkerGetRegistrationCallbacks* callbacks = pending_get_registration_callbacks_.Lookup(request_id);
    DCHECK(callbacks);
    if (!callbacks)
        return;

    callbacks->onError(
        WebServiceWorkerError(error_type, blink::WebString::fromUTF16(message)));
    pending_get_registration_callbacks_.Remove(request_id);
}

void ServiceWorkerDispatcher::OnGetRegistrationsError(
    int thread_id,
    int request_id,
    WebServiceWorkerError::ErrorType error_type,
    const base::string16& message)
{
    TRACE_EVENT_ASYNC_STEP_INTO0(
        "ServiceWorker",
        "ServiceWorkerDispatcher::GetRegistrations",
        request_id,
        "OnGetRegistrationsError");
    TRACE_EVENT_ASYNC_END0("ServiceWorker",
        "ServiceWorkerDispatcher::GetRegistrations",
        request_id);
    WebServiceWorkerGetRegistrationsCallbacks* callbacks = pending_get_registrations_callbacks_.Lookup(request_id);
    DCHECK(callbacks);
    if (!callbacks)
        return;

    callbacks->onError(
        WebServiceWorkerError(error_type, blink::WebString::fromUTF16(message)));
    pending_get_registrations_callbacks_.Remove(request_id);
}

void ServiceWorkerDispatcher::OnEnableNavigationPreloadError(
    int thread_id,
    int request_id,
    WebServiceWorkerError::ErrorType error_type,
    const std::string& message)
{
    WebEnableNavigationPreloadCallbacks* callbacks = enable_navigation_preload_callbacks_.Lookup(request_id);
    DCHECK(callbacks);
    if (!callbacks)
        return;
    callbacks->onError(
        WebServiceWorkerError(error_type, blink::WebString::fromUTF8(message)));
    enable_navigation_preload_callbacks_.Remove(request_id);
}

void ServiceWorkerDispatcher::OnGetNavigationPreloadStateError(
    int thread_id,
    int request_id,
    WebServiceWorkerError::ErrorType error_type,
    const std::string& message)
{
    WebGetNavigationPreloadStateCallbacks* callbacks = get_navigation_preload_state_callbacks_.Lookup(request_id);
    DCHECK(callbacks);
    if (!callbacks)
        return;
    callbacks->onError(
        WebServiceWorkerError(error_type, blink::WebString::fromUTF8(message)));
    get_navigation_preload_state_callbacks_.Remove(request_id);
}

void ServiceWorkerDispatcher::OnSetNavigationPreloadHeaderError(
    int thread_id,
    int request_id,
    WebServiceWorkerError::ErrorType error_type,
    const std::string& message)
{
    WebSetNavigationPreloadHeaderCallbacks* callbacks = set_navigation_preload_header_callbacks_.Lookup(request_id);
    DCHECK(callbacks);
    if (!callbacks)
        return;
    callbacks->onError(
        WebServiceWorkerError(error_type, blink::WebString::fromUTF8(message)));
    set_navigation_preload_header_callbacks_.Remove(request_id);
}

void ServiceWorkerDispatcher::OnServiceWorkerStateChanged(
    int thread_id,
    int handle_id,
    blink::WebServiceWorkerState state)
{
    TRACE_EVENT2("ServiceWorker",
        "ServiceWorkerDispatcher::OnServiceWorkerStateChanged",
        "Thread ID", thread_id,
        "State", state);
    WorkerObjectMap::iterator worker = service_workers_.find(handle_id);
    if (worker != service_workers_.end())
        worker->second->OnStateChanged(state);
}

void ServiceWorkerDispatcher::OnSetVersionAttributes(
    int thread_id,
    int registration_handle_id,
    int changed_mask,
    const ServiceWorkerVersionAttributes& attrs)
{
    TRACE_EVENT1("ServiceWorker",
        "ServiceWorkerDispatcher::OnSetVersionAttributes",
        "Thread ID", thread_id);

    // Adopt the references sent from the browser process and pass it to the
    // registration if it exists.
    std::unique_ptr<ServiceWorkerHandleReference> installing = Adopt(attrs.installing);
    std::unique_ptr<ServiceWorkerHandleReference> waiting = Adopt(attrs.waiting);
    std::unique_ptr<ServiceWorkerHandleReference> active = Adopt(attrs.active);

    RegistrationObjectMap::iterator found = registrations_.find(registration_handle_id);
    if (found != registrations_.end()) {
        // Populate the version fields (eg. .installing) with worker objects.
        ChangedVersionAttributesMask mask(changed_mask);
        if (mask.installing_changed())
            found->second->SetInstalling(
                GetOrCreateServiceWorker(std::move(installing)));
        if (mask.waiting_changed())
            found->second->SetWaiting(GetOrCreateServiceWorker(std::move(waiting)));
        if (mask.active_changed())
            found->second->SetActive(GetOrCreateServiceWorker(std::move(active)));
    }
}

void ServiceWorkerDispatcher::OnUpdateFound(
    int thread_id,
    int registration_handle_id)
{
    TRACE_EVENT0("ServiceWorker",
        "ServiceWorkerDispatcher::OnUpdateFound");
    RegistrationObjectMap::iterator found = registrations_.find(registration_handle_id);
    if (found != registrations_.end())
        found->second->OnUpdateFound();
}

void ServiceWorkerDispatcher::OnSetControllerServiceWorker(
    int thread_id,
    int provider_id,
    const ServiceWorkerObjectInfo& info,
    bool should_notify_controllerchange)
{
    TRACE_EVENT2("ServiceWorker",
        "ServiceWorkerDispatcher::OnSetControllerServiceWorker",
        "Thread ID", thread_id,
        "Provider ID", provider_id);

    // Adopt the reference sent from the browser process and pass it to the
    // provider context if it exists.
    std::unique_ptr<ServiceWorkerHandleReference> handle_ref = Adopt(info);
    ProviderContextMap::iterator provider = provider_contexts_.find(provider_id);
    if (provider != provider_contexts_.end())
        provider->second->OnSetControllerServiceWorker(std::move(handle_ref));

    ProviderClientMap::iterator found = provider_clients_.find(provider_id);
    if (found != provider_clients_.end()) {
        // Get the existing worker object or create a new one with a new reference
        // to populate the .controller field.
        scoped_refptr<WebServiceWorkerImpl> worker = GetOrCreateServiceWorker(
            ServiceWorkerHandleReference::Create(info, thread_safe_sender_.get()));
        found->second->setController(WebServiceWorkerImpl::CreateHandle(worker),
            should_notify_controllerchange);
    }
}

void ServiceWorkerDispatcher::OnPostMessage(
    const ServiceWorkerMsg_MessageToDocument_Params& params)
{
    // Make sure we're on the main document thread. (That must be the only
    // thread we get this message)
    DCHECK_EQ(kDocumentMainThreadId, params.thread_id);
    TRACE_EVENT1("ServiceWorker", "ServiceWorkerDispatcher::OnPostMessage",
        "Thread ID", params.thread_id);

    // Adopt the reference sent from the browser process and get the corresponding
    // worker object.
    scoped_refptr<WebServiceWorkerImpl> worker = GetOrCreateServiceWorker(Adopt(params.service_worker_info));

    ProviderClientMap::iterator found = provider_clients_.find(params.provider_id);
    if (found == provider_clients_.end()) {
        // For now we do no queueing for messages sent to nonexistent / unattached
        // client.
        return;
    }

    blink::WebMessagePortChannelArray ports = WebMessagePortChannelImpl::CreatePorts(
        params.message_ports, params.new_routing_ids,
        base::ThreadTaskRunnerHandle::Get());

    found->second->dispatchMessageEvent(
        WebServiceWorkerImpl::CreateHandle(worker),
        blink::WebString::fromUTF16(params.message), ports);
}

void ServiceWorkerDispatcher::AddServiceWorker(
    int handle_id, WebServiceWorkerImpl* worker)
{
    DCHECK(!base::ContainsKey(service_workers_, handle_id));
    service_workers_[handle_id] = worker;
}

void ServiceWorkerDispatcher::RemoveServiceWorker(int handle_id)
{
    DCHECK(base::ContainsKey(service_workers_, handle_id));
    service_workers_.erase(handle_id);
}

void ServiceWorkerDispatcher::AddServiceWorkerRegistration(
    int registration_handle_id,
    WebServiceWorkerRegistrationImpl* registration)
{
    DCHECK(!base::ContainsKey(registrations_, registration_handle_id));
    registrations_[registration_handle_id] = registration;
}

void ServiceWorkerDispatcher::RemoveServiceWorkerRegistration(
    int registration_handle_id)
{
    DCHECK(base::ContainsKey(registrations_, registration_handle_id));
    registrations_.erase(registration_handle_id);
}

std::unique_ptr<ServiceWorkerRegistrationHandleReference>
ServiceWorkerDispatcher::Adopt(
    const ServiceWorkerRegistrationObjectInfo& info)
{
    return ServiceWorkerRegistrationHandleReference::Adopt(
        info, thread_safe_sender_.get());
}

std::unique_ptr<ServiceWorkerHandleReference> ServiceWorkerDispatcher::Adopt(
    const ServiceWorkerObjectInfo& info)
{
    return ServiceWorkerHandleReference::Adopt(info, thread_safe_sender_.get());
}

} // namespace content
